"""
Input validation functions for document upload
"""
import re
from typing import Tuple, Optional
from datetime import datetime

class DocumentValidator:
    MAX_FILE_SIZE = 50 * 1024 * 1024
    MAX_REGULATION_ID_LENGTH = 64
    MAX_ISSUING_AUTHORITY_LENGTH = 64
    MAX_TAG_LENGTH = 32
    MAX_VERSION_LENGTH = 32
    MAX_TAGS_COUNT = 8
    MAX_FILE_NAME_LENGTH = 128
    
    ALLOWED_FILE_EXTENSIONS = {'.pdf', '.doc', '.docx', '.xlsx', '.txt'}
    
    FILE_NAME_PATTERN = re.compile(r'^[A-Za-z0-9._-]+$')
    REGULATION_ID_PATTERN = re.compile(r'^[A-Za-z0-9._-]+$')
    ISSUING_AUTHORITY_PATTERN = re.compile(r'^[A-Za-z0-9 ]+$')
    TAG_PATTERN = re.compile(r'^[A-Za-z0-9]+$')
    VERSION_PATTERN = re.compile(r'^[A-Za-z0-9._]+$')
    EFFECTIVE_DATE_PATTERN = re.compile(r'^\d{4}[/-]\d{2}[/-]\d{2}$')
    
    @staticmethod
    def validate_file_size(file_size: int) -> Tuple[bool, Optional[str]]:
        if file_size > DocumentValidator.MAX_FILE_SIZE:
            return False, "Document size cannot exceed 50MB"
        return True, None
    
    @staticmethod
    def validate_file_extension(filename: str) -> Tuple[bool, Optional[str]]:
        import os
        ext = os.path.splitext(filename)[1].lower()
        if ext not in DocumentValidator.ALLOWED_FILE_EXTENSIONS:
            return False, f"File type must be one of: {', '.join(DocumentValidator.ALLOWED_FILE_EXTENSIONS)}"
        return True, None
    
    @staticmethod
    def validate_file_name(file_name: str) -> Tuple[bool, Optional[str]]:
        if not file_name:
            return False, "File Name is required"
        
        if len(file_name) > DocumentValidator.MAX_FILE_NAME_LENGTH:
            return False, f"File Name cannot exceed {DocumentValidator.MAX_FILE_NAME_LENGTH} characters"
        
        if not DocumentValidator.FILE_NAME_PATTERN.match(file_name):
            return False, "File Name can only contain letters, numbers, dots (.), underscores (_), and hyphens (-)"
        
        return True, None
    
    @staticmethod
    def validate_regulation_id(regulation_id: str) -> Tuple[bool, Optional[str]]:
        if not regulation_id:
            return True, None
        
        if len(regulation_id) > DocumentValidator.MAX_REGULATION_ID_LENGTH:
            return False, f"Regulation ID cannot exceed {DocumentValidator.MAX_REGULATION_ID_LENGTH} characters"
        
        if not DocumentValidator.REGULATION_ID_PATTERN.match(regulation_id):
            return False, "Regulation ID can only contain letters, numbers, dots (.), and underscores (_)"
        
        return True, None
    
    @staticmethod
    def validate_issuing_authority(authority: str) -> Tuple[bool, Optional[str]]:
        if not authority:
            return True, None
        
        if len(authority) > DocumentValidator.MAX_ISSUING_AUTHORITY_LENGTH:
            return False, f"Issuing Authority cannot exceed {DocumentValidator.MAX_ISSUING_AUTHORITY_LENGTH} characters"
        
        if not DocumentValidator.ISSUING_AUTHORITY_PATTERN.match(authority):
            return False, "Issuing Authority can only contain letters, numbers, and spaces"
        
        return True, None
    
    @staticmethod
    def validate_tag(tag: str) -> Tuple[bool, Optional[str]]:
        if not tag:
            return False, "Tag cannot be empty"
        
        if len(tag) > DocumentValidator.MAX_TAG_LENGTH:
            return False, f"Tag cannot exceed {DocumentValidator.MAX_TAG_LENGTH} characters"
        
        if not DocumentValidator.TAG_PATTERN.match(tag):
            return False, "Tag can only contain letters and numbers"
        
        return True, None
    
    @staticmethod
    def validate_tags_list(tags: list) -> Tuple[bool, Optional[str]]:
        if len(tags) > DocumentValidator.MAX_TAGS_COUNT:
            return False, f"Cannot have more than {DocumentValidator.MAX_TAGS_COUNT} tags"
        
        for tag in tags:
            is_valid, error_msg = DocumentValidator.validate_tag(tag)
            if not is_valid:
                return False, f"Invalid tag '{tag}': {error_msg}"
        
        return True, None
    
    @staticmethod
    def validate_version(version: str) -> Tuple[bool, Optional[str]]:
        if not version:
            return True, None
        
        if len(version) > DocumentValidator.MAX_VERSION_LENGTH:
            return False, f"Version cannot exceed {DocumentValidator.MAX_VERSION_LENGTH} characters"
        
        if not DocumentValidator.VERSION_PATTERN.match(version):
            return False, "Version can only contain letters, numbers, dots (.), and underscores (_)"
        
        return True, None
    
    @staticmethod
    def validate_effective_date(date_str: str) -> Tuple[bool, Optional[str]]:
        if not date_str:
            return False, "Effective Date is required"
        
        if not DocumentValidator.EFFECTIVE_DATE_PATTERN.match(date_str):
            return False, "Date must be in format yyyy/mm/dd or yyyy-mm-dd"
        
        try:
            if '/' in date_str:
                datetime.strptime(date_str, '%Y/%m/%d')
            else:
                datetime.strptime(date_str, '%Y-%m-%d')
            return True, None
        except ValueError:
            return False, "Invalid date"
    
    @staticmethod
    def validate_status(status: str) -> Tuple[bool, Optional[str]]:
        valid_statuses = ['Expired', 'Draft', 'Effective']
        if status not in valid_statuses:
            return False, f"Status must be one of: {', '.join(valid_statuses)}"
        return True, None
    
    @staticmethod
    def validate_required_fields(data: dict) -> Tuple[bool, list]:
        required_fields = ['country', 'document_type', 'issuing_authority', 'collection', 'file_name', 'effective_date', 'status']
        missing_fields = [field for field in required_fields if not data.get(field)]
        return len(missing_fields) == 0, missing_fields
    
    @staticmethod
    def validate_all(file, form_data: dict) -> Tuple[bool, dict]:
        errors = {}
        
        if file:
            is_valid, error = DocumentValidator.validate_file_size(file.content_length if hasattr(file, 'content_length') else 0)
            if not is_valid:
                errors['file'] = error
            
            is_valid, error = DocumentValidator.validate_file_extension(file.filename)
            if not is_valid:
                errors['file_type'] = error
        else:
            errors['file'] = "No file provided"
        
        is_valid, missing = DocumentValidator.validate_required_fields(form_data)
        if not is_valid:
            errors['required'] = f"Information not fully filled in. Missing: {', '.join(missing)}"
        
        is_valid, error = DocumentValidator.validate_file_name(form_data.get('file_name', ''))
        if not is_valid:
            errors['file_name'] = error
        
        is_valid, error = DocumentValidator.validate_regulation_id(form_data.get('regulation_id', ''))
        if not is_valid:
            errors['regulation_id'] = error
        
        is_valid, error = DocumentValidator.validate_issuing_authority(form_data.get('issuing_authority', ''))
        if not is_valid:
            errors['issuing_authority'] = error
        
        tags = form_data.get('tags', [])
        if isinstance(tags, str):
            tags = [t.strip() for t in tags.split(',') if t.strip()]
        is_valid, error = DocumentValidator.validate_tags_list(tags)
        if not is_valid:
            errors['tags'] = error
        
        is_valid, error = DocumentValidator.validate_version(form_data.get('version', ''))
        if not is_valid:
            errors['version'] = error
        
        is_valid, error = DocumentValidator.validate_effective_date(form_data.get('effective_date', ''))
        if not is_valid:
            errors['effective_date'] = error
        
        is_valid, error = DocumentValidator.validate_status(form_data.get('status', ''))
        if not is_valid:
            errors['status'] = error
        
        return len(errors) == 0, errors